﻿using System;
using NL.ADA.Common;
using System.IO;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.ServiceModel;
using NL.ADA.ConfigProxy.NotificationServiceReference;
using NL.ADA.Logging;


namespace NL.ADA.ConfigProxy
{
    public sealed class Proxy : IDisposable
    {


        public static string AdaConfigLocalFileName = System.Configuration.ConfigurationManager.AppSettings["applicationcode"].ToString() + "adaconfig.ada";
        private string _applicationCode = System.Configuration.ConfigurationManager.AppSettings["applicationcode"];
        private string _mode = System.Configuration.ConfigurationManager.AppSettings["mode"];
        private NotificationServiceClient _notification;
        public int _serverChanged;
        private static AdaLogger _logger = new AdaLogger();

        private Proxy()
        {
            _logger.Log(LogEvent.EnterEvent);
            Initialize();
            _logger.Log(LogEvent.LeaveEvent);
        }

        private void Initialize()
        {
            _logger.Log(LogEvent.EnterEvent);

            if (!HasLocalStorage)
            {
                ConfigTemplate serverTemplate = GetServerConfigTemplate(this._applicationCode, this._mode);
                StoreLocalConfigTemplate(serverTemplate);
            }


            var callBack = new ImplementationCallback();
            callBack.ServerModifiedNotify += new EventHandler<ServerModifiedEventArgs>(callBack_ServerModifiedNotify);
            var context = new InstanceContext(callBack);
            _notification = new NotificationServiceReference.NotificationServiceClient(context);
            RegisterNotification();
            _logger.Log(LogEvent.LeaveEvent);

        }

        private void Refresh()
        {
            _logger.Log(LogEvent.EnterEvent);
            ConfigTemplate serverTemplate = GetServerConfigTemplate(this._applicationCode, this._mode);
            StoreLocalConfigTemplate(serverTemplate);


            if (Proxy.Instance != null)
            {
                ConfigTemplate tempStorage = Proxy.Instance.GetLocalConfigTemplate(AdaConfigLocalFileName);
                //ommit concurrency:
                Proxy._storage = tempStorage;
            }

            //update ConfigurationManager:
            ConfigurationManager.Refresh();
            ConfigurationManager<object>.Refresh();
            _logger.Log(LogEvent.LeaveEvent);
        }


        public void callBack_ServerModifiedNotify(object sender, ServerModifiedEventArgs e)
        {
            _logger.Log(LogEvent.EnterEvent);


            _serverChanged = e.Modified.CompareTo(DateTime.MinValue);
            Refresh();


            _logger.Log(LogEvent.LeaveEvent);
        }

        public int ModeId { get; set; }
        public int ApplicationId { get; set; }


        public bool ServerModified()
        {
            _logger.Log(LogEvent.EnterEvent);
            var gateWay = new Gateway();
            var modified = gateWay.GetServerModified(this.ApplicationId, this.ModeId);
            _logger.Log(LogEvent.LeaveEvent);
            return modified > Storage.Modified;
        }

        public ConfigTemplate GetLocalConfigTemplate(string fileName)
        {
            _logger.Log(LogEvent.EnterEvent);
            var template = new ConfigTemplate();
            var isoStore = IsolatedStorageFile.GetUserStoreForAssembly();
            IsolatedStorageFileStream localStream = null;
            try
            {
                if (isoStore.GetFileNames(AdaConfigLocalFileName).Length > 0)
                {
                    localStream = new IsolatedStorageFileStream(fileName, FileMode.Open, FileAccess.Read, isoStore);
                    IFormatter formatter = new BinaryFormatter();
                    template = (ConfigTemplate)formatter.Deserialize(localStream);

                }

            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                if (localStream != null)
                    localStream.Close();
            }
            _logger.Log(LogEvent.LeaveEvent);
            return template;
        }

        private bool HasLocalStorage
        {
            get
            {

                var isoStore = IsolatedStorageFile.GetUserStoreForAssembly();
                return isoStore.GetFileNames(AdaConfigLocalFileName).Length > 0;

            }
        }

        public ConfigTemplate GetServerConfigTemplate(string appCode, string mode)
        {
            _logger.Log(LogEvent.EnterEvent);
            var template = new ConfigTemplate();

            template = new ConfigTemplate();
            var gateWay = new Gateway();
            var appId = gateWay.GetApplicationId(appCode);
            var modeId = gateWay.GetModeId(mode);
            var list = gateWay.GetAllConfigs(appId, modeId);
            template.ApplicationId = appId;
            template.ModeId = modeId;
            template.Config = list;


            _logger.Log(LogEvent.LeaveEvent);
            return template;
        }


        public void DeleteLocalConfigTemplateFile(string fileName)
        {
            _logger.Log(LogEvent.EnterEvent);
            IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForAssembly();
            if (isoStore.GetFileNames(fileName).Length > 0)
            {
                isoStore.DeleteFile(fileName);
            }
            _logger.Log(LogEvent.LeaveEvent);
        }

        public bool StoreLocalConfigTemplate(ConfigTemplate template)
        {
            _logger.Log(LogEvent.EnterEvent);
            bool result = false;
            IsolatedStorageFileStream stream = null;
            IsolatedStorageFile isoStore = null;
            try
            {
                isoStore = IsolatedStorageFile.GetUserStoreForAssembly();
                IFormatter formatter = new BinaryFormatter();
                stream = new IsolatedStorageFileStream(AdaConfigLocalFileName, FileMode.OpenOrCreate, isoStore);
                formatter.Serialize(stream, template);
                result = true;
            }
            catch (Exception)
            {

                throw;
            }
            finally
            {
                if (stream != null)
                    stream.Close();
                if (isoStore != null)
                    isoStore.Close();
            }
            _logger.Log(LogEvent.LeaveEvent);
            return result;

        }


        static readonly Proxy _instance = new Proxy();
        public static Proxy Instance
        {
            get
            {
                return _instance;
            }
        }
        static ConfigTemplate _storage = Proxy.Instance.GetLocalConfigTemplate(AdaConfigLocalFileName);
        public ConfigTemplate Storage
        {
            get
            {
                return _storage;
            }
        }

        private bool RegisterNotification()
        {
            _logger.Log(LogEvent.EnterEvent);
            bool result;
            try
            {
                _notification.Open();
                result = _notification.Register(this.Storage.ApplicationId, this.Storage.ModeId);

            }
            catch (Exception ex)
            {
                _logger.Log(LogEvent.ErrorEvent, ex);
                result = false;
            }
            _logger.Log(LogEvent.LeaveEvent);
            return result;
        }

        private void UnRegisterNotification()
        {
            _logger.Log(LogEvent.EnterEvent);
            try
            {
                if (_notification != null)
                {
                    if (_notification.State != CommunicationState.Opened)
                    {
                        throw new Exception("cannot unregister.notification channel is closed");
                    }
                    if (!_notification.UnRegister(this.Storage.ApplicationId, this.Storage.ModeId))
                    {
                        throw new Exception("cannot unregister for notification");
                    }

                }
                else
                {
                    throw new Exception("notification callback object is null");
                }

            }
            catch (Exception ex)
            {
                _logger.Log(LogEvent.ErrorEvent, ex);
                throw;
            }
            _logger.Log(LogEvent.LeaveEvent);
        }

        #region IDisposable Members

        public void Dispose()
        {
            _logger.Log(LogEvent.EnterEvent);
            UnRegisterNotification();
            _notification.Close();
            _logger.Log(LogEvent.LeaveEvent);
        }

        #endregion
    }
}
